package com.apidoc.utis;


import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.apidoc.annotation.Api;
import com.apidoc.bean.*;
import com.apidoc.common.Const;
import com.apidoc.common.VO;
import com.github.stuxuhai.jpinyin.PinyinException;
import com.github.stuxuhai.jpinyin.PinyinFormat;
import com.github.stuxuhai.jpinyin.PinyinHelper;

import java.lang.reflect.Method;
import java.util.*;

/**
 * @Description: 生成API文档工具类
 * @Author: admin
 * @CreateDate: 2018/1/4 15:04
 */
public class GeneratorUtil {

    /**
     * 获取文档基本信息
     *
     * @return String 基本信息
     */
    public static JSONObject getInfo() {
        //读取硬盘信息
        String info = JsonUtil.reader(Const.info);
        if (StringUtil.isEmpty(info)) {
            info = JsonUtil.toJsonString(new Info());
            //写入硬盘
            JsonUtil.writer(Const.info, info);
        }
        return com.alibaba.fastjson.JSONObject.parseObject(info);
    }

    /**
     * 获取模块信息
     */
    public static List<Module> getModules(String packageName) {
        JSONObject beanPO = JsonUtil.readerObject(Const.modulesFile);///读取硬盘数据
        Set<Module> moduleSet = new HashSet<>();//保存返回前端的数据

        //扫描包得到类
        Set<Class> classSet = ClassScanUtil.getClass4Annotation(packageName, Api.class);
        if (classSet.isEmpty()) {
            throw new RuntimeException("该包下没有class文件： " + packageName);
        } else {
            for (Class claszz : classSet) {
                //获取元数据
                Api api = (Api) claszz.getAnnotation(Api.class);
                String name = StringUtil.isNotEmpty(api.value()) ? api.value() : claszz.getName();//没有写明模块名称时 默认取类名全称 com.xxx.xxx
                Integer order = Integer.MAX_VALUE;//默认排序int最大值

                //处理要持久化的数据
                JSONObject bean = beanPO.getJSONObject(name);
                //处理要持久化的数据
                if (bean != null) {//取到数据
                    JSONArray classList = bean.getJSONArray("classList");
                    Set<Object> set = new HashSet<>(classList);//防止重名
                    set.add(claszz.getName());
                    bean.put("classList", set);
                    beanPO.put(name, bean);
                    order = bean.getInteger("order");
                } else {//没有取到数据
                    List<String> classList = new ArrayList<>(1);
                    classList.add(claszz.getName());
                    JSONObject po = new JSONObject();
                    po.put("classList", classList);
                    po.put("order", order);
                    beanPO.put(name, po);
                }

                //处理返回前端的数据
                Module module = new Module();
                module.setName(name);
                module.setOrder(order);
                moduleSet.add(module);
            }
        }
        //保存到硬盘
        JsonUtil.writer(Const.modulesFile, beanPO);
        //排序 并返回前端 默认按名称排序 如果存在order则按order排序
        List<Module> moduleList = new ArrayList<>(moduleSet);
        moduleList.sort(Comparator.comparing(Module::getName));
        moduleList.sort(Comparator.comparing(Module::getOrder));
        return moduleList;
    }

    /**
     * 获取接口列表信息
     * 获取模块下 所有类 的所有public方法 组成的信息
     *
     * @param moduleName 模块名称
     * @return List<Action>
     */
    public static List<Action> getMethods(String moduleName) {
        String path = null;//保存的路径
        try {
            path = Const.actionFile +  PinyinHelper.convertToPinyinString(moduleName, "-", PinyinFormat.WITHOUT_TONE) + ".json";
//            System.out.println(path);
        } catch (PinyinException e) {
            e.printStackTrace();
        }
        //获得模块下的所有的类
        JSONObject modulePO = JsonUtil.readerObject(Const.modulesFile); //todo 可以存放到内存 后期做
        JSONArray classList = modulePO.getJSONObject(moduleName).getJSONArray("classList");
        if (classList != null && classList.size() > 0) {
            List<Action> actions = new ArrayList<>();//存放返回前端的接口数据列表
            JSONObject actionsPO = JsonUtil.readerObject(path);//读取硬盘的接口列表数据

            for (int i = 0; i < classList.size(); i++) {
                String className = classList.getString(i);
                //获得该类文件的所有public方法的注释
                Map<String, String> methodNotes = StringUtil.getMethodNotes(className);
                Class claszz = ClassUtil.getClassByName(className);
                //获得本类的所有public方法
                if (claszz != null) {
                    Method[] methods = claszz.getMethods();
                    Class superclass = claszz.getSuperclass();
                    Method[] superclassMethods = superclass.getMethods();
                    List<Method> methodList = new LinkedList<>(Arrays.asList(methods));
                    List<Method> superclassMethodsList = new LinkedList<>(Arrays.asList(superclassMethods));
                    methodList.removeAll(superclassMethodsList);

                    //获得该类的接口信息
                    if (methodList.size() > 0) {
                        List<Action> actionList = new ArrayList<>(methods.length);
                        for (Method method : methodList) {
                            String methodName = claszz.getName() + "-" + method.getName();//方法名 格式：全类名-方法名
                            //返回前端的数据
                            Action action = new Action();
                            action.setMethodName(methodName);
                            JSONObject po = actionsPO.getJSONObject(methodName);//硬盘存放的数据
                            //得到方法的注释信息
                            String name = methodNotes.get(methodName);
                            if (po == null) {
                                //有注释时用注释 没有时默认方法的名称
                                action.setName(StringUtil.isEmpty(name) ? method.getName() : name);
                                action.setOrder(Integer.MAX_VALUE);
                            } else {
                                //硬盘保存的信息存在时用硬盘信息 否则用注释
                                action.setName(StringUtil.isNotEmpty(po.getString("name")) ? po.getString("name") : name);
                                action.setOrder(po.getInteger("order"));
                            }
                            //添加到list
                            actionList.add(action);
                        }
                        //添加到总actions
                        actions.addAll(actionList);
                    }

                }
            }

            //排序 默认按名称排序 存在order时,order优先排序
            actions.sort(Comparator.comparing(Action::getName));
            actions.sort(Comparator.comparing(Action::getOrder));

            //保存到硬盘
            VO bean = new VO(actions.size());//转Vo
            actions.forEach(m -> bean.set(m.getMethodName(), new VO(2).set("name", m.getName()).set("order", m.getOrder())));
            JsonUtil.writer(path, bean);
            return actions;
        }
        return null;
    }

    /**
     * 根据方法名唯一标识 获取 该方法的文档信息
     *
     * @param methodUUID 方法名的唯一标识
     * @return List<Details>
     */
    public static Detail getDetail(String methodUUID) {

        String[] split = methodUUID.split("-");//拆分类名和方法名
        if (split.length == 2) {
            String className = split[0];
            Class claszz = ClassUtil.getClassByName(className);
            if (claszz != null) {
                String methodName = split[1];
                //todo 方法可能存在重载，后期处理
                // 即：重名相同但是参数列表不同（参数的类型 数量 顺序不同）
                Method method = ClassUtil.getMethod(claszz, methodName);
                if (method != null) {
                    String mapping = SpringUtil.getMapping(claszz) + SpringUtil.getMapping(method);//url 映射 mapping 为类上的mapping+方法上的mapping
                    String requestMethod = SpringUtil.getRequestMethod(method);//请求方式
                    Params requestParams = ClassUtil.getParams(method, methodUUID);
                    requestParams.setMethodUUID(methodUUID);
                    Params responseParams = ClassUtil.getReturn(method, methodUUID);
                    responseParams.setMethodUUID(methodUUID + "-return");

                    //返回前端数据
                    Detail detail = new Detail();
                    detail.setMapping(mapping);
                    detail.setRequestMethod(requestMethod);
                    detail.setDescription(JsonUtil.reader(Const.actionDescription + methodUUID));

                    detail.setRequestParam(requestParams);
                    detail.setResponseParam(responseParams);
                    return detail;
                }
            }
        }
        return null;
    }

}
